/*!
 * \file WtPorter.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 *
 * \brief
 */
#include "WtPorter.h"
#include "WtRtRunner.h"

#include "../Includes/WTSTradeDef.hpp"
#include "../Includes/WTSVersion.h"
#include "../WTSTools/WTSLogger.h"
#include "../WtCore/WtHelper.h"

#ifdef _WIN32
#ifdef _WIN64
char PLATFORM_NAME[] = "X64";
#else
char PLATFORM_NAME[] = "X86";
#endif
#else
char PLATFORM_NAME[] = "UNIX";
#endif

WtRtRunner& getRunner() {
    static WtRtRunner runner;
    return runner;
}

void register_evt_callback(FuncEventCallback cbEvt) {
    getRunner().registerEvtCallback(cbEvt);
}

void register_cta_callbacks(
    FuncStraInitCallback cbInit, FuncStraTickCallback cbTick,
    FuncStraCalcCallback cbCalc, FuncStraBarCallback cbBar,
    FuncSessionEvtCallback cbSessEvt,
    FuncStraCondTriggerCallback cbCondTrigger /* = NULL*/) {
    getRunner().registerCtaCallbacks(cbInit, cbTick, cbCalc, cbBar, cbSessEvt,
                                     cbCondTrigger);
}

void register_parser_callbacks(FuncParserEvtCallback cbEvt,
                               FuncParserSubCallback cbSub) {
    getRunner().registerParserPorter(cbEvt, cbSub);
}

void register_exec_callbacks(FuncExecInitCallback cbInit,
                             FuncExecCmdCallback cbExec) {
    getRunner().registerExecuterPorter(cbInit, cbExec);
}

bool create_ext_parser(const char* id) {
    return getRunner().createExtParser(id);
}

bool create_ext_executer(const char* id) {
    return getRunner().createExtExecuter(id);
}

void register_ext_data_loader(FuncLoadFnlBars fnlBarLoader,
                              FuncLoadRawBars rawBarLoader,
                              FuncLoadAdjFactors fctLoader,
                              FuncLoadRawTicks tickLoader) {
    getRunner().registerExtDataLoader(fnlBarLoader, rawBarLoader, fctLoader,
                                      tickLoader);
}

void feed_raw_bars(WTSBarStruct* bars, WtUInt32 count) {
    getRunner().feedRawBars(bars, count);
}

void feed_adj_factors(WtString stdCode, WtUInt32* dates, double* factors,
                      WtUInt32 count) {
    getRunner().feedAdjFactors(stdCode, (uint32_t*)dates, factors, count);
}

void feed_raw_ticks(WTSTickStruct* ticks, WtUInt32 count) {
    WTSLogger::error("API not implemented");
}

void init_porter(const char* logProfile, bool isFile, const char* genDir) {
    static bool inited = false;

    if (inited)
        return;

    getRunner().init(logProfile, isFile, genDir);

    inited = true;
}

void config_porter(const char* cfgfile, bool isFile) {
    if (strlen(cfgfile) == 0)
        getRunner().config("config.json", true);
    else
        getRunner().config(cfgfile, isFile);
}

void run_porter(bool bAsync) { getRunner().run(bAsync); }

void release_porter() { getRunner().release(); }

const char* get_version() {
    static std::string _ver;
    if (_ver.empty()) {
        _ver = PLATFORM_NAME;
        _ver += " ";
        _ver += WT_VERSION;
        _ver += " Build@";
        _ver += __DATE__;
        _ver += " ";
        _ver += __TIME__;
    }
    return _ver.c_str();
}

const char* get_raw_stdcode(const char* stdCode) {
    return getRunner().get_raw_stdcode(stdCode);
}

void write_log(WtUInt32 level, const char* message, const char* catName) {
    if (strlen(catName) > 0) {
        WTSLogger::log_raw_by_cat(catName, (WTSLogLevel)level, message);
    }
    else {
        WTSLogger::log_raw((WTSLogLevel)level, message);
    }
}

bool reg_cta_factories(const char* factFolder) {
    return getRunner().addCtaFactories(factFolder);
}

bool reg_exe_factories(const char* factFolder) {
    return getRunner().addExeFactories(factFolder);
}

#pragma region "CTA策略接口"

CtxHandler create_cta_context(const char* name, int slippage) {
    return getRunner().createCtaContext(name, slippage);
}

void cta_enter_long(CtxHandler cHandle, const char* stdCode, double qty,
                    const char* userTag, double limitprice, double stopprice) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->stra_enter_long(stdCode, qty, userTag, limitprice, stopprice);
}

void cta_exit_long(CtxHandler cHandle, const char* stdCode, double qty,
                   const char* userTag, double limitprice, double stopprice) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->stra_exit_long(stdCode, qty, userTag, limitprice, stopprice);
}

void cta_enter_short(CtxHandler cHandle, const char* stdCode, double qty,
                     const char* userTag, double limitprice, double stopprice) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->stra_enter_short(stdCode, qty, userTag, limitprice, stopprice);
}

void cta_exit_short(CtxHandler cHandle, const char* stdCode, double qty,
                    const char* userTag, double limitprice, double stopprice) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->stra_exit_short(stdCode, qty, userTag, limitprice, stopprice);
}

WtUInt32 cta_get_bars(CtxHandler cHandle, const char* stdCode,
                      const char* period, WtUInt32 barCnt, bool isMain,
                      FuncGetBarsCallback cb) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;
    try {
        WTSKlineSlice* kData =
            ctx->stra_get_bars(stdCode, period, barCnt, isMain);
        if (kData) {
            WtUInt32 reaCnt = (WtUInt32)kData->size();

            uint32_t blkCnt = kData->get_block_counts();
            for (uint32_t i = 0; i < blkCnt; i++) {
                if (kData->get_block_addr(i) != NULL)
                    cb(cHandle, stdCode, period, kData->get_block_addr(i),
                       kData->get_block_size(i), i == blkCnt - 1);
            }

            kData->release();
            return reaCnt;
        }
        else {
            return 0;
        }
    } catch (...) {
        return 0;
    }
}

WtUInt32 cta_get_ticks(CtxHandler cHandle, const char* stdCode,
                       WtUInt32 tickCnt, FuncGetTicksCallback cb) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;
    try {
        WTSTickSlice* tData = ctx->stra_get_ticks(stdCode, tickCnt);
        if (tData) {
            uint32_t thisCnt = min(tickCnt, (WtUInt32)tData->size());
            cb(cHandle, stdCode, (WTSTickStruct*)tData->at(0), thisCnt, true);
            tData->release();
            return thisCnt;
        }
        else {
            return 0;
        }
    } catch (...) {
        return 0;
    }
}

double cta_get_position_profit(CtxHandler cHandle, const char* stdCode) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_position_profit(stdCode);
}

WtUInt64 cta_get_detail_entertime(CtxHandler cHandle, const char* stdCode,
                                  const char* openTag) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_detail_entertime(stdCode, openTag);
}

double cta_get_detail_cost(CtxHandler cHandle, const char* stdCode,
                           const char* openTag) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_detail_cost(stdCode, openTag);
}

double cta_get_detail_profit(CtxHandler cHandle, const char* stdCode,
                             const char* openTag, int flag) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_detail_profit(stdCode, openTag, flag);
}

double cta_get_position_avgpx(CtxHandler cHandle, const char* stdCode) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_position_avgpx(stdCode);
}

void cta_get_all_position(CtxHandler cHandle, FuncGetPositionCallback cb) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL) {
        cb(cHandle, "", 0, true);
        return;
    }

    ctx->enum_position([cb, cHandle](const char* stdCode, double qty) {
        cb(cHandle, stdCode, qty, false);
    });

    cb(cHandle, "", 0, true);
}

double cta_get_position(CtxHandler cHandle, const char* stdCode,
                        bool bOnlyValid, const char* openTag) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_position(stdCode, bOnlyValid, openTag);
}

double cta_get_fund_data(CtxHandler cHandle, int flag) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_fund_data(flag);
}

void cta_set_position(CtxHandler cHandle, const char* stdCode, double qty,
                      const char* userTag, double limitprice,
                      double stopprice) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->stra_set_position(stdCode, qty, userTag, limitprice, stopprice);
}

WtUInt64 cta_get_first_entertime(CtxHandler cHandle, const char* stdCode) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_first_entertime(stdCode);
}

WtUInt64 cta_get_last_entertime(CtxHandler cHandle, const char* stdCode) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_last_entertime(stdCode);
}

WtUInt64 cta_get_last_exittime(CtxHandler cHandle, const char* stdCode) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_last_exittime(stdCode);
}

double cta_get_last_enterprice(CtxHandler cHandle, const char* stdCode) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_last_enterprice(stdCode);
}

WtString cta_get_last_entertag(CtxHandler cHandle, const char* stdCode) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_last_entertag(stdCode);
}

double cta_get_price(const char* stdCode) {
    return getRunner().getEngine()->get_cur_price(stdCode);
}

double cta_get_day_price(const char* stdCode, int flag) {
    return getRunner().getEngine()->get_day_price(stdCode, flag);
}

WtUInt32 cta_get_tdate() { return getRunner().getEngine()->get_trading_date(); }

WtUInt32 cta_get_date() { return getRunner().getEngine()->get_date(); }

WtUInt32 cta_get_time() { return getRunner().getEngine()->get_min_time(); }

void cta_log_text(CtxHandler cHandle, WtUInt32 level, const char* message) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    switch (level) {
    case LOG_LEVEL_DEBUG:
        ctx->stra_log_debug(message);
        break;
    case LOG_LEVEL_INFO:
        ctx->stra_log_info(message);
        break;
    case LOG_LEVEL_WARN:
        ctx->stra_log_warn(message);
        break;
    case LOG_LEVEL_ERROR:
        ctx->stra_log_error(message);
        break;
    default:
        break;
    }
}

void cta_save_userdata(CtxHandler cHandle, const char* key, const char* val) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->stra_save_user_data(key, val);
}

WtString cta_load_userdata(CtxHandler cHandle, const char* key,
                           const char* defVal) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return defVal;

    return ctx->stra_load_user_data(key, defVal);
}

void cta_sub_ticks(CtxHandler cHandle, const char* stdCode) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->stra_sub_ticks(stdCode);
}

void cta_set_chart_kline(CtxHandler cHandle, const char* stdCode,
                         const char* period) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->set_chart_kline(stdCode, period);
}

void cta_add_chart_mark(CtxHandler cHandle, double price, const char* icon,
                        const char* tag) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->add_chart_mark(price, icon, tag);
}

void cta_register_index(CtxHandler cHandle, const char* idxName,
                        WtUInt32 indexType) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return;

    ctx->register_index(idxName, indexType);
}

bool cta_register_index_line(CtxHandler cHandle, const char* idxName,
                             const char* lineName, WtUInt32 lineType) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return false;

    return ctx->register_index_line(idxName, lineName, lineType);
}
bool cta_add_index_baseline(CtxHandler cHandle, const char* idxName,
                            const char* lineName, double val) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return false;

    return ctx->add_index_baseline(idxName, lineName, val);
}

bool cta_set_index_value(CtxHandler cHandle, const char* idxName,
                         const char* lineName, double val) {
    CtaContextPtr ctx = getRunner().getCtaContext(cHandle);
    if (ctx == NULL)
        return false;

    return ctx->set_index_value(idxName, lineName, val);
}

#pragma endregion

#pragma region "扩展Parser接口"
void parser_push_quote(const char* id, WTSTickStruct* curTick,
                       WtUInt32 uProcFlag) {
    getRunner().on_ext_parser_quote(id, curTick, uProcFlag);
}
#pragma endregion "扩展Parser接口"